#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/limits.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

#define MODE 			(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
#define BUF_SIZE		4096
#define OPTIONS_SIZE 	8

static char options[OPTIONS_SIZE] = { 0 };

static inline void err_fatal(const char* msg) {
	perror(msg);
	exit(-1);
}

void mycp(const char* srcpath, const char* dstpath) {
	char tmp_dstpath[PATH_MAX];
	struct stat sb;
	int ret, srcfd, dstfd, dstflags = O_WRONLY | O_TRUNC;
	char option;

	// 查看源文件是否存在
	ret = stat(srcpath, &sb);
	if (ret == -1) {
		err_fatal(srcpath);
	}

	// 源文件是目录
	if (S_ISDIR(sb.st_mode)) {
		fprintf(stderr, "%s is a directory\n", srcpath);
		exit(-1);
	}

	// 查看目标路径是否存在
	ret = stat(dstpath, &sb);
	if (ret == -1) {					// 目标不存在
		if (errno != ENOENT) {
			err_fatal(dstpath);
		}
		
		dstflags |= O_CREAT | O_EXCL;	// 假设其为文件
	}
	else {								// 目标存在
		if (S_ISDIR(sb.st_mode)) {		// 为目录
			int dstlen = strlen(dstpath);
			int srclen = strlen(srcpath);
			int i;

			strcpy(tmp_dstpath, dstpath);
			if (tmp_dstpath[dstlen - 1] != '/') {
				tmp_dstpath[dstlen] = '/';
				dstlen++;
			}

			for (i = srclen - 1; i >= 0; i--) {
				if (srcpath[i] == '/') {
					break;
				}
			}

			strcpy(tmp_dstpath + dstlen, srcpath + i + 1);
			dstpath = tmp_dstpath;
		}

		ret = stat(dstpath, &sb);
		if (ret == -1) {
			if (errno != ENOENT) {
				err_fatal(dstpath);
			}
			
			dstflags |= O_CREAT | O_EXCL;
		}
	}

	if (dstflags & O_CREAT) {
		dstfd = open(dstpath, dstflags, MODE);
	}
	else {
		dstfd = open(dstpath, dstflags);
	}
	if (dstfd == -1) {
		err_fatal(dstpath);
	}

	srcfd = open(srcpath, O_RDONLY);
	if (srcfd == -1) {
		err_fatal(srcpath);
	}

	char buf[BUF_SIZE];
	ssize_t nread, nwrite;

	while ((nread = read(srcfd, buf, BUF_SIZE)) > 0) {
		nwrite = write(dstfd, buf, nread);
		if (nwrite < nread) {
			fprintf(stderr, "%s\n", "nwrite < nread");
			exit(-1);
		}
	}
	if (nread == -1) {
		err_fatal("read");
	}

	close(srcfd);
	close(dstfd);
}
